package com.modern.devtools.service;

import com.modern.devtools.interfaces.CommandExecutor;
import com.modern.devtools.model.DeploymentProfile;
import com.modern.devtools.model.Server;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Deployment Service - manages deployment configurations and execution
 */
@Service
public class DeploymentService {
    
    private final ObservableList<DeploymentProfile> deploymentProfiles = FXCollections.observableArrayList();
    private final ConcurrentHashMap<String, DeploymentProfile> profileMap = new ConcurrentHashMap<>();
    
    @Autowired
    private ServerService serverService;
    
    private final ExecutorService deploymentExecutor = Executors.newFixedThreadPool(5);
    
    /**
     * Adds a new deployment profile
     * @param profile The deployment profile to add
     */
    public void addDeploymentProfile(DeploymentProfile profile) {
        if (!profileMap.containsKey(profile.getId())) {
            deploymentProfiles.add(profile);
            profileMap.put(profile.getId(), profile);
        }
    }
    
    /**
     * Updates an existing deployment profile
     * @param profile The deployment profile with updated information
     */
    public void updateDeploymentProfile(DeploymentProfile profile) {
        DeploymentProfile existingProfile = profileMap.get(profile.getId());
        if (existingProfile != null) {
            deploymentProfiles.remove(existingProfile);
            profileMap.put(profile.getId(), profile);
            deploymentProfiles.add(profile);
        }
    }
    
    /**
     * Removes a deployment profile
     * @param profileId The ID of the profile to remove
     */
    public void removeDeploymentProfile(String profileId) {
        DeploymentProfile profile = profileMap.remove(profileId);
        if (profile != null) {
            deploymentProfiles.remove(profile);
        }
    }
    
    /**
     * Gets a deployment profile by ID
     * @param profileId The ID of the profile to retrieve
     * @return Optional containing the profile if found
     */
    public Optional<DeploymentProfile> getDeploymentProfileById(String profileId) {
        return Optional.ofNullable(profileMap.get(profileId));
    }
    
    /**
     * Gets all deployment profiles
     * @return ObservableList of all deployment profiles
     */
    public ObservableList<DeploymentProfile> getAllDeploymentProfiles() {
        return FXCollections.unmodifiableObservableList(deploymentProfiles);
    }
    
    /**
     * Executes a deployment using the specified profile
     * @param profileId The ID of the profile to use for deployment
     * @return true if deployment was initiated successfully
     */
    public boolean executeDeployment(String profileId) {
        Optional<DeploymentProfile> profileOpt = getDeploymentProfileById(profileId);
        if (profileOpt.isPresent()) {
            DeploymentProfile profile = profileOpt.get();
            
            // Update status to IN_PROGRESS
            profile.setStatus("IN_PROGRESS");
            
            deploymentExecutor.submit(() -> {
                try {
                    // Execute the deployment steps
                    boolean success = performDeployment(profile);
                    
                    // Update profile status and last deployed time
                    profile.setStatus(success ? "SUCCESS" : "FAILED");
                    profile.setLastDeployed(LocalDateTime.now());
                    
                    if (success) {
                        System.out.println("Deployment successful for profile: " + profile.getName());
                    } else {
                        System.err.println("Deployment failed for profile: " + profile.getName());
                    }
                } catch (Exception e) {
                    System.err.println("Deployment error for profile " + profile.getName() + ": " + e.getMessage());
                    e.printStackTrace();
                    profile.setStatus("FAILED");
                }
            });
            
            return true;
        }
        return false;
    }
    
    /**
     * Performs the actual deployment steps
     * @param profile The deployment profile to use
     * @return true if all steps succeeded, false otherwise
     */
    private boolean performDeployment(DeploymentProfile profile) {
        try {
            // 1. Build step (if build command is specified)
            if (profile.getBuildCommand() != null && !profile.getBuildCommand().trim().isEmpty()) {
                System.out.println("Executing build command: " + profile.getBuildCommand());
                // In a real implementation, you would execute the build command on a build server
            }
            
            // 2. For each target server in the profile
            for (String serverId : profile.getTargetServers()) {
                Optional<Server> serverOpt = serverService.getServerById(serverId);
                if (serverOpt.isPresent()) {
                    Server server = serverOpt.get();
                    
                    // 3. If deploy script is specified, execute it
                    if (profile.getDeployScript() != null && !profile.getDeployScript().trim().isEmpty()) {
                        System.out.println("Executing deployment script on server: " + server.getName());
                        CommandExecutor.ExecutionResult result = 
                            serverService.executeCommand(serverId, profile.getDeployScript());
                        
                        if (!result.isSuccess()) {
                            System.err.println("Deployment script failed on server " + server.getName() + 
                                ": " + result.getError());
                            return false;
                        }
                    } else {
                        // 4. Default deployment: upload files to target path
                        System.out.println("Deploying to server: " + server.getName());
                        // In a real implementation, you would upload the built artifacts
                    }
                } else {
                    System.err.println("Target server not found: " + serverId);
                    return false;
                }
            }
            
            return true;
        } catch (Exception e) {
            System.err.println("Deployment error: " + e.getMessage());
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * Gets the number of deployment profiles
     * @return Number of deployment profiles
     */
    public int getDeploymentProfileCount() {
        return deploymentProfiles.size();
    }
    
    /**
     * Gets the number of profiles currently being deployed
     * @return Number of active deployments
     */
    public int getActiveDeploymentCount() {
        return (int) deploymentProfiles.stream()
                .filter(profile -> "IN_PROGRESS".equals(profile.getStatus()))
                .count();
    }
}